package com.ng.biz.service.impl;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import com.influxdb.client.InfluxDBClient;
import com.influxdb.client.InfluxDBClientFactory;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.dialect.MysqlDialect;
import com.jfinal.plugin.activerecord.dialect.OracleDialect;
import com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect;
import com.jfinal.plugin.activerecord.dialect.SqlServerDialect;
import com.jfinal.plugin.activerecord.dialect.Sqlite3Dialect;
import com.jfinal.plugin.druid.DruidPlugin;
import com.ng.biz.model.domain.Datasource;
import com.ng.biz.model.domain.DatasourceGroup;
import com.ng.biz.service.IDatasource;
import com.ng.common.enums.DatasourceTypeEnum;
import com.ng.common.model.response.R;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class DatasourceImpl implements IDatasource {

	private final ConcurrentHashMap<String, DruidPlugin> Running_DruidPlugin = new ConcurrentHashMap<String, DruidPlugin>();
	private final ConcurrentHashMap<String, ActiveRecordPlugin> Running_ActiveRecordPlugin = new ConcurrentHashMap<String, ActiveRecordPlugin>();
	
	private final ConcurrentHashMap<String, InfluxDBClient> Running_InfluxDBClient = new ConcurrentHashMap<String, InfluxDBClient>();
	
	@Override
	public R<String> deleteDatasourceGroupByIds(String ids) {
		String[] arr = ids.split(",");
		if (ids == null || ids.length() <= 0) {
			return R.failed("请选择需要删除的数据源分组.");
		}
		for (String id : arr) {
			deleteChildrens(id);
			Db.delete("delete from " + Datasource.TABLE_NAME + " where group_id=?", id);
			DatasourceGroup.dao.deleteById(id);
		}
		return R.ok();
	}

	private void deleteChildrens(String id) {
		List<DatasourceGroup> datasourceGroups = DatasourceGroup.dao.find("select id from " + DatasourceGroup.TABLE_NAME + " where parent_id=?", id);
		for (DatasourceGroup datasourceGroup : datasourceGroups) {
			deleteChildrens(datasourceGroup.getId());
			Db.delete("delete from " + Datasource.TABLE_NAME + " where group_id=?", datasourceGroup.getId());
			datasourceGroup.delete();
		}
	}

	@Override
	public String isRunning(String id) {
		return Running_DruidPlugin.containsKey(id) || Running_InfluxDBClient.containsKey(id) ? "1" : "0";
	}

	@Override
	public R<String> test(String id) {
		Datasource dataSource = Datasource.dao.findById(id);
		if (dataSource == null) {
			return R.failed("数据源不存在.");
		}
		JSONObject json = JSONObject.parseObject(dataSource.getConfig());
		if (json == null) {
			return R.failed("数据源配置有误.");
		}
		String url = json.getString("url");
		String username = json.getString("user");
		String password = json.getString("password");
		Connection connection = null;
		try {
			connection = DriverManager.getConnection(url, username, password);
			if (connection != null) {
				connection.close();
				return R.ok("连接成功.");
			}
			return R.failed("连接失败.");
		} catch (SQLException e) {
			return R.failed(e.getMessage());
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (SQLException ex) {
				}
			}
		}
		//return R.failed("未知数据源类型【" + dataSource.getType() + "】.");
	}

	@Override
	public R<String> start(String id) {
		Datasource dataSource = Datasource.dao.findById(id);
		if (dataSource == null) {
			return R.failed("数据源不存在.");
		}
		return start(dataSource) ? R.ok() : R.failed();
	}

	@Override
	public R<String> stop(String id) {
		Datasource dataSource = Datasource.dao.findById(id);
		if (dataSource == null) {
			return R.failed("数据源不存在.");
		}
		return stop(dataSource) ? R.ok() : R.failed();
	}
	
	
	private boolean start(Datasource datasource) {
		
		JSONObject json = JSONObject.parseObject(datasource.getConfig());
		if (json == null) {
			log.error(String.format("数据源【%s】配置有误.", datasource.getName()));
			return false;
		}
		
		
		if(StrKit.equals("InfluxDB", datasource.getType())) {
			InfluxDBClient influxDBClient=InfluxDBClientFactory.create(json.getString("url"), json.getString("token").toCharArray(), json.getString("org"), json.getString("bucket"));
			Running_InfluxDBClient.put(datasource.getId(), influxDBClient);
			return true;
		}
		
		
		DruidPlugin druidPlugin = new DruidPlugin(json.getString("url"), json.getString("user"),json.getString("password"));
		
		if(!StrKit.equals(DatasourceTypeEnum.SQLITE.getType(), datasource.getType())) {
			
			if (json.containsKey("initialSize")) {
				druidPlugin.setInitialSize(json.getIntValue("initialSize"));
			}

			if (json.containsKey("minIdle")) {
				druidPlugin.setMinIdle(json.getIntValue("minIdle"));
			}

			if (json.containsKey("maxActive")) {
				druidPlugin.setMaxActive(json.getIntValue("maxActive"));
			}

			if (json.containsKey("maxWait")) {
				druidPlugin.setMaxWait(json.getIntValue("maxWait"));
			}
		}
		
		ActiveRecordPlugin arp = new ActiveRecordPlugin(datasource.getId(), druidPlugin);
		if (json.containsKey("showSql")) {
			arp.setShowSql(StrKit.equals("1", json.getString("showSql")));
		} else {
			arp.setShowSql(false);
		}
		
		
		if (StrKit.equals(DatasourceTypeEnum.ORACLE.getType(), datasource.getType())) {
			//druidPlugin.setDriverClass(bean.getDriverClass());
			arp.setDialect(new OracleDialect());
		} else if (StrKit.equals(DatasourceTypeEnum.MYSQL.getType(), datasource.getType())) {
			arp.setDialect(new MysqlDialect());
		} else if (StrKit.equals(DatasourceTypeEnum.SQLSERVER.getType(), datasource.getType())) {
			arp.setDialect(new SqlServerDialect());
		} else if (StrKit.equals(DatasourceTypeEnum.SQLITE.getType(), datasource.getType())) {
			//druidPlugin.setDriverClass(bean.getDriverClass());
			arp.setDialect(new Sqlite3Dialect());
		} else if (StrKit.equals(DatasourceTypeEnum.POSTGRESQL.getType(), datasource.getType())) {
			//druidPlugin.setDriverClass(bean.getDriverClass());
			arp.setDialect(new PostgreSqlDialect());
		}
		
		arp.setTransactionLevel(Connection.TRANSACTION_READ_COMMITTED);
		arp.getEngine().setToClassPathSourceFactory();

		boolean isDruidPlugin = druidPlugin.start();
		boolean isActiveRecordPlugin = arp.start();

		if (isDruidPlugin && isActiveRecordPlugin) {
			Running_DruidPlugin.put(datasource.getId(), druidPlugin);
			Running_ActiveRecordPlugin.put(datasource.getId(), arp);
		}
		return isDruidPlugin && isActiveRecordPlugin;
	}
	
	private boolean stop(Datasource dataSource) {
		if (Running_DruidPlugin.containsKey(dataSource.getId())) {
			Running_DruidPlugin.get(dataSource.getId()).stop();
			Running_DruidPlugin.remove(dataSource.getId());
		}

		if (Running_ActiveRecordPlugin.containsKey(dataSource.getId())) {
			Running_ActiveRecordPlugin.get(dataSource.getId()).stop();
			Running_ActiveRecordPlugin.remove(dataSource.getId());
		}
		
		
		if (Running_InfluxDBClient.containsKey(dataSource.getId())) {
			Running_InfluxDBClient.get(dataSource.getId()).close();
			Running_InfluxDBClient.remove(dataSource.getId());
		}
		return true;
	}

	@Override
	public void startAll() {
		List<Datasource> dataSourceList = Datasource.dao
				.find("select * from " + Datasource.TABLE_NAME + " where enabled='1'");
		for (Datasource dataSource : dataSourceList) {
			start(dataSource);
		}
	}

	@Override
	public void stopAll() {
		List<Datasource> dataSourceList = Datasource.dao
				.find("select * from " + Datasource.TABLE_NAME + " where enabled='1'");
		for (Datasource dataSource : dataSourceList) {
			stop(dataSource);
		}
	}

	@Override
	public void startDefaultBoot() {
		List<Datasource> dataSourceList = Datasource.dao
				.find("select * from " + Datasource.TABLE_NAME + " where enabled='1' and default_boot='1'");
		for (Datasource dataSource : dataSourceList) {
			start(dataSource);
		}
	}

	@Override
	public InfluxDBClient getInfluxDBClient(String id) {
		return Running_InfluxDBClient.get(id);
	}

}
